﻿using System.Reflection;

namespace ECommon
{
    /// <summary>
    /// 对象工具
    /// </summary>
    public static class ObjectTool
    {
        /// <summary>
        /// 深拷贝
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="obj">初始对象</param>
        /// <returns>被拷贝的对象</returns>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static T DeepCopy<T>(T obj)
        {
            if (obj == null || obj is string || obj.GetType().IsValueType)
            {
                return obj;
            }
            T result = Activator.CreateInstance<T>();
            if (result == null)
            {
                throw new ArgumentException("生成的对象为空");
            }
            FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            foreach (FieldInfo field in fields)
            {
                object? fielldObj = field.GetValue(obj);
                if (fielldObj == null)
                {
                    continue;
                }
                SetFieldValue(field, result, fielldObj);
            }
            PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            foreach (PropertyInfo property in properties)
            {
                object? propertyObj = property.GetValue(obj);
                if (propertyObj == null)
                {
                    continue;
                }
                SetPropertyValue(property, result, propertyObj);
            }
            return result;
        }

        /// <summary>
        /// 比较两个对象是否相等
        /// </summary>
        /// <param name="val1">被比较对象</param>
        /// <param name="val2">比较对象</param>
        /// <returns>比较结果</returns>
        public static bool Eq(object val1, object val2)
        {
            if (val1 == null && val2 == null)
            {
                return true;
            }
            else if (val1!=null&& val2 != null)
            {
                Type type1 = val1.GetType();
                Type type2 = val2.GetType();
                if (type1.FullName != type2.FullName)
                {
                    return false;
                }
                if (type1 == typeof(string) || type1.IsValueType)
                {
                    //return ConvertTool.GetObjString(val1)==ConvertTool.GetObjString(val2);
                    return val1.ToString() == val2.ToString();
                }
                return object.Equals(val1, val2);
                //string str1 = ConvertTool.GetString(val1);
                //string str2 = ConvertTool.GetString(val2);
                //return str1 == str2;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 为目标对象设置字段值
        /// </summary>
        /// <param name="fi">字段信息</param>
        /// <param name="destiny">目标对象</param>
        /// <param name="value">字段值</param>
        /// <returns>设置结果</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static bool SetFieldValue(FieldInfo fi, object destiny, object value)
        {
            if (fi == null || destiny == null)
            {
                throw new ArgumentNullException("fi或者destiny", "字段和目标为空");
            }
            string tmp = fi.FieldType.Name;
            if (string.IsNullOrWhiteSpace(tmp))
            {
                throw new ArgumentException("字段信息类型出错");
            }
            switch (tmp)
            {
                case "Boolean":
                    fi.SetValue(destiny, Convert.ToBoolean(value));
                    break;

                case "Byte":
                    fi.SetValue(destiny, Convert.ToByte(value));
                    break;

                case "Char":
                    fi.SetValue(destiny, Convert.ToChar(value));
                    break;

                case "Decimal":
                    fi.SetValue(destiny, Convert.ToDecimal(value));
                    break;

                case "Double":
                    fi.SetValue(destiny, Convert.ToDouble(value));
                    break;

                case "Single":
                    fi.SetValue(destiny, Convert.ToSingle(value));
                    break;

                case "Int32":
                    fi.SetValue(destiny, Convert.ToInt32(value));
                    break;

                case "Int64":
                    fi.SetValue(destiny, Convert.ToInt64(value));
                    break;

                case "SByte":
                    fi.SetValue(destiny, Convert.ToSByte(value));
                    break;

                case "Int16":
                    fi.SetValue(destiny, Convert.ToInt16(value));
                    break;

                case "UInt32":
                    fi.SetValue(destiny, Convert.ToUInt32(value));
                    break;

                case "UInt64":
                    fi.SetValue(destiny, Convert.ToUInt64(value));
                    break;

                case "UInt16":
                    fi.SetValue(destiny, Convert.ToInt16(value));
                    break;

                case "DateTime":
                    fi.SetValue(destiny, Convert.ToDateTime(value));
                    break;

                case "Nullable_Boolean":
                case "Nullable_Byte":
                case "Nullable_Char":
                case "Nullable_Decimal":
                case "Nullable_Double":
                case "Nullable_Single":
                case "Nullable_Int32":
                case "Nullable_Int64":
                case "Nullable_SByte":
                case "Nullable_Int16":
                case "Nullable_UInt32":
                case "Nullable_UInt64":
                case "Nullable_UInt16":
                case "Nullable_DateTime":
                case "String":
                    fi.SetValue(destiny, value);
                    break;

                default:
                    throw new ArgumentException(string.Format("不支持类型:{0}", tmp), tmp);
            }
            return true;
        }

        /// <summary>
        /// 为目标对象设置属性值
        /// </summary>
        /// <param name="pi">属性信息</param>
        /// <param name="destiny">目标对象</param>
        /// <param name="value">属性值</param>
        /// <returns>设置结果</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static bool SetPropertyValue(PropertyInfo pi, object destiny, object value)
        {
            if (pi == null || destiny == null)
            {
                throw new ArgumentNullException("pi或者destiny", "属性信息或者目标对象为空");
            }
            string tmp = pi.PropertyType.Name;
            if (string.IsNullOrWhiteSpace(tmp))
            {
                throw new ArgumentException("属性信息类型出错");
            }
            switch (tmp)
            {
                case "Boolean":
                    pi.SetValue(destiny, Convert.ToBoolean(value), null);
                    break;

                case "Byte":
                    pi.SetValue(destiny, Convert.ToByte(value), null);
                    break;

                case "Char":
                    pi.SetValue(destiny, Convert.ToChar(value), null);
                    break;

                case "Decimal":
                    pi.SetValue(destiny, Convert.ToDecimal(value), null);
                    break;

                case "Double":
                    pi.SetValue(destiny, Convert.ToDouble(value), null);
                    break;

                case "Single":
                    pi.SetValue(destiny, Convert.ToSingle(value), null);
                    break;

                case "Int32":
                    pi.SetValue(destiny, Convert.ToInt32(value), null);
                    break;

                case "Int64":
                    pi.SetValue(destiny, Convert.ToInt64(value), null);
                    break;

                case "SByte":
                    pi.SetValue(destiny, Convert.ToSByte(value), null);
                    break;

                case "Int16":
                    pi.SetValue(destiny, Convert.ToInt16(value), null);
                    break;

                case "UInt32":
                    pi.SetValue(destiny, Convert.ToUInt32(value), null);
                    break;

                case "UInt64":
                    pi.SetValue(destiny, Convert.ToUInt64(value), null);
                    break;

                case "UInt16":
                    pi.SetValue(destiny, Convert.ToUInt16(value), null);
                    break;

                case "DateTime":
                    pi.SetValue(destiny, Convert.ToDateTime(value), null);
                    break;

                case "Nullable_Boolean":
                case "Nullable_Byte":
                case "Nullable_Char":
                case "Nullable_Decimal":
                case "Nullable_Double":
                case "Nullable_Single":
                case "Nullable_Int32":
                case "Nullable_Int64":
                case "Nullable_SByte":
                case "Nullable_Int16":
                case "Nullable_UInt32":
                case "Nullable_UInt64":
                case "Nullable_UInt16":
                case "Nullable_DateTime":
                case "String":
                    pi.SetValue(destiny, value, null);
                    break;

                default:
                    throw new ArgumentException(string.Format("不支持的类型:{0}", tmp), tmp);
            }
            return true;
        }
    }
}